﻿using System;
using BizElements.Core;

namespace BizElements.BusinessLayer
{
    /// <summary>
    /// Base class for <see cref="ISearchEngineBuilder"/> implementations which use <b>BizElements</b> generated classes.
    /// </summary>
    /// <typeparam name="TTable"><b>BizElements</b> generated table/meta class which contains metadata for table searched by the engine created by this builder.</typeparam>
    [Serializable]
    public abstract class SearchEngineBuilderBase<TTable> : ISearchEngineBuilder
        where TTable : IDbTable, new()
    {
        #region CTor.

        /// <summary>
        /// Initializes a new instance of the <b>EngineBuilderBase(TTAble)</b> class.
        /// </summary>
        public SearchEngineBuilderBase()
        {          
            this.Table = new TTable();
        }

        #endregion

        #region Table.

        /// <summary>
        /// Gets the table searched by the engine created by this builder.
        /// </summary>
        public TTable Table { get; private set; }

        #endregion

        #region Private connection-transaction context.

        /// <summary>
        /// This member is only accessed and initialized through _ConnectionProvider getter.
        /// </summary>
        [NonSerialized]
        IConnectionProvider conn;

        /// <summary>
        /// Connection provider used by data-access methods. May be shared between methods. 
        /// Eg. validate and save business object using the same open connection.
        /// </summary>
        protected virtual IConnectionProvider _ConnectionProvider
        {
            get
            {
                if (this.conn == null)
                    this.conn = Table.Catalog.CreateConnectionProvider();
                return this.conn;
            }
        }

        #endregion

        #region MessageSource.

        [NonSerialized]
        ITextSource messageSource;
        string messageSourceCultureName;

        /// <summary>
        /// A method which ensures that the message source is initialized.
        /// </summary>
        /// <param name="textSource">A reference to a non-serializable class field points an ITextSource object or null. 
        /// If the field points to a null reference, the method will initialize it.</param>
        /// <param name="cultureName">A reference to a serializable class field which contains the name of the culture used by the <b>textSource</b>.</param>
        public delegate void TextSourceInitializer(ref ITextSource textSource, ref string cultureName);

        /// <summary>
        /// Gets a delegate to a method which ensures that the message source is initialized.
        /// </summary>
        /// <remarks>Must be implemented in derived classes.</remarks>
        protected virtual TextSourceInitializer EnsureMessageSource
        {
            get { throw new NotImplementedException("Must be implemented in derived classes."); }
        }

        /// <summary>
        /// Source of messages emitted by a business object. Eg. validation messages, captions.
        /// </summary>
        public ITextSource MessageSource
        {
            get
            {
                EnsureMessageSource(ref this.messageSource, ref this.messageSourceCultureName);
                return this.messageSource;
            }
            set
            {
                this.messageSource = value;
                this.messageSourceCultureName = this.messageSource.CurrentCulture.Name;
            }
        }

        #endregion

        #region ICaptionProvider.

        /// <summary>
        /// Gets the string which is prefixes the property name in the text source.
        /// </summary>
        /// <value>A string. Default is the name of the name of the table in the database, ie. the value of the Table.TableName property.</value>
        protected virtual string MessageKeyPrefix
        {
            get { return this.Table.TableName; }
        }

        /// <summary>
        /// Gets the string which delimites the MessageKeyPrefix from the property name in the text source.
        /// </summary>
        /// <value>A string. Default is "_".</value>
        protected virtual string MessageKeyDelimiter
        {
            get { return "_"; }
        }

        /// <summary>
        /// Gets the caption for the specified property.
        /// </summary>
        /// <param name="propertyName">Property.</param>
        /// <returns>Caption. String.Empty if it doesn't exist in the text source.</returns>
        public virtual string GetFieldCaption(string propertyName)
        {
            string caption = this.MessageSource.GetString(this.MessageKeyPrefix + this.MessageKeyDelimiter + propertyName);
            return (caption != null) ? caption : "";
        }

        /// <summary>
        /// Gets the caption for the specified field.
        /// </summary>
        /// <param name="field">Field.</param>
        /// <returns>Caption. String.Empty if it doesn't exist in the text source.</returns>
        public virtual string GetFieldCaption(IDbColumn field)
        {
            return GetFieldCaption(field.PropertyName);
        }

        #endregion

        #region CreateSearchEngine.

        /// <summary>
        /// Creates a search engine for a specific search task.
        /// </summary>
        /// <param name="actorRequestingData">Actor which is requesting data. May be used to restrict data available to actor.</param>
        /// <returns>Search engine.</returns>
        /// <remarks>Must be implemented in derived classes.</remarks>
        public virtual ISearchEngine CreateSearchEngine(IActor actorRequestingData)
        {
            throw new NotImplementedException("Must be implemented in derived classes.");
        }

        #endregion
    }
}